import { ESJResource, ESJVector3DArray, ESTreeParam, ESVisibilityAnalysis, getMinMaxCorner, JsonProperty } from "earthsdk3";
import { Event, PartialWithUndefinedReactivePropsToNativeProps, ReactivePropsToNativePropsAndChanged, extendClassProps, nextAnimateFrame } from "earthsdk3";
import { SceneObjectKey } from "earthsdk3";
import * as turf from '@turf/turf';
import { UeESForest } from "./UeESForest";
export type newESJTreeParam = {
    rotation: [number, number, number]
} & ESTreeParam
export type ESJTree = {
    id: string,
    treeType: string,
    position: [number, number, number],
    guanfu: [number, number] | number,
    xiongjing: [number, number] | number,
    zhixiagao: number,
    shugao: number,
}
export type ESJTreeType = {
    id: string,
    name: string,
    cesiumAsset: string,
    ueAsset: string,
    createdAt: string,
    dongxiguanfu: number,
    nanbeiguanfu: number,
    shugao: number,
    updatedAt: string,
    xiongjing: number,
    zhixiagao: number,
    cesiumAssetServerPath: string,
    ueAssetServerPath: string,
    treeModelClassId: string
}
export class ESForest extends ESVisibilityAnalysis {
    static override readonly type = this.register('ESForest', this, { chsName: 'Forest使用的实例化森林对象', tags: ['ESObjects', '_ES_Impl_Cesium', '_ES_Impl_UE'], description: "实例化森林" });
    get typeName() { return 'ESForest'; }
    override get defaultProps() { return ESForest.createDefaultProps(); }
    get json() { return this._innerGetJson() as JsonType; }
    set json(value: JsonType) { this._innerSetJson(value); }

    static override defaults = {
        ...ESVisibilityAnalysis.defaults,
    }

    public trees: ESJTree[] = [];
    public treesParam: newESJTreeParam[] = [];
    private _treesEvent = this.ad(new Event());
    get treesEvent() { return this._treesEvent; }

    public highLightTreeId: string[] = []; // 高亮树id

    public treeTypes: { [key: string]: ESJTreeType[] } = {};
    public treeTypesAll: ESJTreeType[] = []; // 所有树类型
    public treeTypesUE: { name: string, meshPath: string }[] = []; // ue树类型
    public treeTypesCesium: { name: string, meshPath: string }[] = []; // cesium树类型
    private _treeTypesEvent = this.ad(new Event());
    get treeTypesEvent() { return this._treeTypesEvent; }
    /**
     * 每次调用会重新生成，一般情况下只有url改变时才会用到这个
     */
    getTreeTypes(viewer?: "Cesium" | "UE"): (ESJTreeType | { name: string, meshPath: string })[] {
        const tempTreeTypes = [];
        for (const key in this.treeTypes) {
            const tempArr = this.treeTypes[key];
            for (let i = 0; i < tempArr.length; i++) {
                const element = tempArr[i];
                if (viewer) {
                    const meshPath = element[viewer.toLocaleLowerCase() == 'cesium' ? "cesiumAssetServerPath" : "ueAsset"];
                    if (meshPath == "") continue;
                    tempTreeTypes.push({
                        "name": key + (i + 1).toString(),
                        "meshPath": meshPath
                    });
                } else {
                    tempTreeTypes.push({
                        ...element,
                        "name": key + (i + 1).toString()
                    })
                }
            }
        }
        return tempTreeTypes;
    }

    /**
   * 格式化数据
   * @param trees 
   * @param treeTypes 
   */
    formatData(trees: ESJTree[], treeTypes: ESJTreeType[]): newESJTreeParam[] {
        return trees.map(item => {
            let closestType: ESJTreeType | null = null;
            let diff = Infinity;
            let treeTypeId = 0
            // 遍历所有类型，找到与当前树最接近的
            treeTypes.forEach((e, index) => {
                if (e.name.replace(/\d+$/, '') == item.treeType) {
                    const currentDiff = Math.abs(e.shugao - item.shugao);
                    if (currentDiff < diff) {
                        diff = currentDiff;
                        closestType = e;
                        treeTypeId = index
                    }
                }
            });

            // 根据树高最接近的树类型，进行变换
            if (closestType) {
                // @ts-ignore
                const scaleZ = item.shugao / closestType.shugao
                let obj = {
                    id: item.id,
                    location: item.position,
                    rotation: [Math.random() * 360, 0, 0] as [number, number, number],
                    treeTypeId: treeTypeId,
                    scale: [scaleZ, scaleZ, scaleZ] as [number, number, number],
                };
                return obj
            } else {
                return undefined
            }
        }).filter(e => e != undefined)

    }

    // 添加林木数据
    private _addEvent = this.disposeVar(new Event<[newESJTreeParam[]]>());
    get addEvent() { return this._addEvent; }
    add(trees: ESJTree[] | newESJTreeParam[]) {
        const switchDate = trees.length != 0 && Reflect.has(trees[0], "treeTypeId") ?
            trees as newESJTreeParam[] :
            this.formatData(trees as ESJTree[], this.treeTypesAll);
        this.addEvent.emit(switchDate);
    }
    // 更新林木数据
    private _updateEvent = this.disposeVar(new Event<[newESJTreeParam[]]>());
    get updateEvent() { return this._updateEvent; }
    update(trees: ESJTree[] | newESJTreeParam[]) {
        const switchDate = trees.length != 0 && Reflect.has(trees[0], "treeTypeId") ?
            trees as newESJTreeParam[] :
            this.formatData(trees as ESJTree[], this.treeTypesAll);
        this.updateEvent.emit(switchDate);
    }
    // 删除林木数据
    private _removeEvent = this.disposeVar(new Event<[string[]]>());
    get removeEvent() { return this._removeEvent; }
    remove(treeIds: string[]) { this.removeEvent.emit(treeIds); }
    private _treeShowEvent = this.disposeVar(new Event<[string[], boolean]>());
    get treeShowEvent() { return this._treeShowEvent; }
    treeShow(treeIds: string[], show: boolean) { this.treeShowEvent.emit(treeIds, show); }
    // 清空所有数据
    private _removeAllEvent = this.disposeVar(new Event());
    get removeAllEvent() { return this._removeAllEvent; }
    removeAll() { this.removeAllEvent.emit(); }
    // 砍倒林木
    private _cutDownEvent = this.disposeVar(new Event<[string[], number]>());
    get cutDownEvent() { return this._cutDownEvent; }
    cutDown(treeIds: string[], timeLength: number) { this.cutDownEvent.emit(treeIds, timeLength); }
    // 生长模拟
    private _growthEvent = this.disposeVar(new Event<[newESJTreeParam[], number]>());
    get growthEvent() { return this._growthEvent; }
    growth(trees: ESJTree[], timeLength: number = 3) {
        const switchDate = this.formatData(trees, this.treeTypesAll);
        this.growthEvent.emit(switchDate, timeLength);
    }
    // 旋转树木
    private _rotateTreeEvent = this.disposeVar(new Event<[string]>());
    get rotateTreeEvent() { return this._rotateTreeEvent; }
    rotateTree(treeId: string) { this._rotateTreeEvent.emit(treeId); }
    // 移动树木
    private _moveTreeEvent = this.disposeVar(new Event<[string]>());
    get moveTreeEvent() { return this._moveTreeEvent; }
    moveTree(treeId: string) { this._moveTreeEvent.emit(treeId); }
    // 高亮树木
    private _highlightTreeEvent = this.disposeVar(new Event<[string | string[]]>());
    get highlightTreeEvent() { return this._highlightTreeEvent; }
    highlightTree(treeId: string | string[]) {
        this.highLightTreeId = Array.isArray(treeId) ? treeId : [treeId];
        this._highlightTreeEvent.emit(treeId);
    }
    // 飞行至某棵树
    private _flyToTreeEvent = this.disposeVar(new Event<[string]>());
    get flyToTreeEvent() { return this._flyToTreeEvent; }
    flyToTree(treeId: string) { this._flyToTreeEvent.emit(treeId); }
    // 矩形框选
    queryByRectangle(points: [[number, number, number], [number, number, number]]) {
        this.queryByPolygon(points, true);
    }
    queryByPolygon(points: ESJVector3DArray, isRectangle?: boolean) {
        if (points.length == 0) return [];
        // 查找符合要求的treeIds
        let polygon = turf.polygon([points]);
        if (isRectangle) {
            const { minPos, maxPos } = getMinMaxCorner(points)
            polygon = turf.polygon([[
                [minPos[0], minPos[1]],
                [minPos[0], maxPos[1]],
                [maxPos[0], maxPos[1]],
                [maxPos[0], minPos[1]],
                [minPos[0], minPos[1]]
            ]]);
        }
        const trees: newESJTreeParam[] = [];
        for (const key in this.treesParam) {
            const treeParam = this.treesParam[key];
            const point = turf.point([treeParam.location[0], treeParam.location[1]]);
            if (turf.booleanPointInPolygon(point, polygon)) {
                trees.push(treeParam);
            }
        }
        // 对treeIds去重
        const uniqueTreeIds = Array.from(new Set(trees));
        // this.highlightTree(uniqueTreeIds.map(e => e.id));
        return uniqueTreeIds;
    }

    // pick
    async pickTree(viewer: any, screenPosition: [number, number]) {
        const result = await viewer.pick(screenPosition);
        const sceneObject = result.sceneObject;
        if (!sceneObject) return;
        if ((sceneObject.id !== this.id) && (sceneObject.typeName != "ESFeTreeEditing")) return;
        if (this.viewer?.getEngineType() == "ESUeViewer") {
            const ueObj = this.viewer.getEngineObject(this) as UeESForest;
            if (result.pickResult) {
                const ueRes = await ueObj.eSSeparateFoliage.getIdByComponentNameAndHitItem(
                    this.viewer,
                    result.pickResult.componentName,
                    result.pickResult.hitItem
                )
                if (ueRes.error == "" && ueRes.re && ueRes.re.TreeId) {
                    result.pickResult.treeId = ueRes.re.TreeId;
                    return result
                }
            }
        } else {
            if (result.pickResult) {
                result.pickResult.treeId = result.pickResult.primitive.structuralMetadata.getPropertyTable(0).getProperty(result.pickResult.featureId, "id");
                return result;
            }
        }
    }


    constructor(id?: SceneObjectKey) {
        super(id);
        this.allowPicking = true;
        {
            const update = () => {
                this.trees.length !== 0 && this.add(this.trees)
            }
            update();
            this.ad(this.treesEvent.don(() => {
                this.ad(nextAnimateFrame(update));
            }))
        }
        {
            const update = () => {
                this.trees = [];
                this.treesParam = [];
                this.treeTypes = {};
                this.treeTypesAll = [];
                this.treeTypesUE = [];
                this.treeTypesCesium = [];
                this.removeAll(); // 清空所有数据
                if (!this.url) return;
                const url = typeof this.url == 'string' ? this.url : this.url.url;
                if (url == "") return;
                fetch(url).then(res => res.json()).then(data => {
                    this.treeTypes = data.data.types;
                    this.treeTypesAll = this.getTreeTypes() as ESJTreeType[];
                    this.treeTypesUE = this.getTreeTypes("UE") as { name: string, meshPath: string }[];
                    this.treeTypesCesium = this.getTreeTypes("Cesium") as { name: string, meshPath: string }[];
                    this.treeTypesEvent.emit();
                    const temp = [] as ESJTree[];
                    const treeTypesStr = this.treeTypesAll.reduce((pre, cur) => { return pre + cur.name }, "");
                    data.data.trees.forEach((item: any) => {
                        if (!treeTypesStr.includes(item.treeType)) return; // 过滤掉不存在的树类型
                        temp.push({
                            id: item.id,
                            treeType: item.treeType,
                            position: item.position,
                            guanfu: [item.dongxiguanfu, item.nanbeiguanfu],
                            xiongjing: item.xiongjing,
                            zhixiagao: item.zhixiagao,
                            shugao: item.shugao
                        })
                    });;
                    this.trees = temp;
                    this.treesParam = this.formatData(this.trees, this.treeTypesAll);
                    this.treesEvent.emit();
                }).catch(err => {
                    console.log('数据读取失败', err);
                })
            }
            this.ad(this.urlChanged.don(update))
        }
    }
    override getESProperties() {
        const properties = { ...super.getESProperties() };
        return {
            ...properties,
            coordinate: [
                ...properties.coordinate,
                new JsonProperty('路径', '路径', false, false, [this, 'url']),
            ]
        };
    };
    override getProperties(language: string) {
        return [
            ...super.getProperties(language),
            new JsonProperty('路径', '路径', false, false, [this, 'url']),
        ];
    }


}

export namespace ESForest {
    export const createDefaultProps = () => ({
        ...ESVisibilityAnalysis.createDefaultProps(),
        url: undefined as string | ESJResource | undefined,
    });
}
extendClassProps(ESForest.prototype, ESForest.createDefaultProps);
export interface ESForest extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof ESForest.createDefaultProps>> { }
type JsonType = PartialWithUndefinedReactivePropsToNativeProps<ReturnType<typeof ESForest.createDefaultProps> & { type: string }>;